package com.heo.util;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;
import org.springframework.util.StringUtils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.heo.pojo.ControlInfoEntity;
import com.heo.pojo.StatusDictEntity;
import com.heo.pub.DictEnum;
import com.heo.pub.ServerConfig;
import com.heo.task.CallBackTaskQueue;
import com.heo.task.thread.CallBackInfo;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;

public class NettyChannelMapUtils {

	private static Logger logger = Logger.getLogger(NettyChannelMapUtils.class);
	private static Map<String, Channel> channelMaps = new ConcurrentHashMap<String, Channel>();
	// 目前有3种key
	// [DEVICE_CONTROL]appId+":"+imei号，这个key是用来保存设备的控制信息和状态。
	// [CALLBACK_INFO]appId+":"+imei号+":"+通道号，这个key是用来保存设备通道的回调信息
	// [LOCK_STATUS]appId+":"+imei号+":"+通道号，这个key是用来保存设备的锁状态（主要用来实现预约功能）
	public static final String DEVICE_CONTROL = "[DEVICE_CONTROL]";// 主要作用是防止保存的redis里的信息和其他的系统产生冲突
	private static final String CALLBACK_INFO = "[CALLBACK_INFO]";// 控制设备时，发过来的回调参数
	private static final String DEVICE_SCAN_INFO = "[DEVICE_SCAN_INFO]";// 设备扫码，发过来的操作指令
	// 线程池
//	public static ExecutorService sendTaskThreadPool = Executors.newFixedThreadPool(30);// 发送任务线程池
	
	public static ThreadPoolExecutor sendTaskThreadPool = new ThreadPoolExecutor(10, 100, 5, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(100));

	public static synchronized void add(String appId, String deviceIMEI, int deviceChannelNum, short deviceType,
			Channel socketChannel) {
		try {
			String deviceImei = DEVICE_CONTROL + appId + ":" + deviceIMEI;
			if (Utils.checkNull(deviceImei) && channelMaps.containsKey(deviceImei)) {
				Channel sHandle = get(appId, deviceIMEI);
				if (sHandle != null) {
					channelMaps.remove(deviceImei);
					sHandle.unsafe().close(sHandle.unsafe().voidPromise());
					sHandle = null;
				}
			}
			channelMaps.put(deviceImei, socketChannel);
			ControlInfoEntity controlInfo = new ControlInfoEntity();
			controlInfo.setControlUrl(ServerConfig.CONTROL_URL);
			controlInfo.setDeviceIMEI(deviceIMEI);
			controlInfo.setDeviceType(deviceType);
			controlInfo.setSocketChannelId(socketChannel.id().asShortText());

			for (int i = 0; i < deviceChannelNum; i++) {
				controlInfo.addChannelInfo(i, DictEnum.DeviceChannelStatusCode.FREE);
			}

			// 把设备的信息放到redis里
			String redisJson = JSON.toJSONString(controlInfo);
			RedisUtil.addValue(deviceImei, redisJson, ServerConfig.READ_IDLE_TIME);
			logger.debug("写入redis信息：" + redisJson);

			// String timeStampByMinute = Utils.timeStampByMinute();
			// if(timeStampByMinute.endsWith("0") || timeStampByMinute.endsWith("2") ||
			// timeStampByMinute.endsWith("4") || timeStampByMinute.endsWith("6") ||
			// timeStampByMinute.endsWith("8")) {
			// JSONObject sendJson = new JSONObject();
			// sendJson.put("imei", deviceIMEI);
			// CallBackInfo callBackInfo = new CallBackInfo(sendJson.toString(),
			// ServerConfig.SUB_HTTPS_URL + "?imei=" + deviceIMEI);
			// CallBackTaskQueue.getInstance().addCallBackMsg(callBackInfo);
			// }
		} catch (Exception e) {
			logger.error("NettyChannelMapUtils.add()出错，参数信息如下：");
			logger.error("channelMaps.size()=" + channelMaps.size());
			logger.error(e.getMessage(), e);
		}
	}

	public static synchronized Channel get(String appId, String deviceIMEI) {
		String deviceImei = DEVICE_CONTROL + appId + ":" + deviceIMEI;
		if (deviceImei != null && !"".equals(deviceImei)) {
			return channelMaps.get(deviceImei);
		}
		return null;
	}

	public static synchronized void remove(String appId, String deviceIMEI) {
		String deviceImei = DEVICE_CONTROL + appId + ":" + deviceIMEI;
		Channel sHandle = get(appId, deviceIMEI);
		if (sHandle != null) {
			channelMaps.remove(deviceImei);
			sHandle.unsafe().close(sHandle.unsafe().voidPromise());
			sHandle = null;
			RedisUtil.remove(deviceImei);
			logger.info("netty remove imei = " + deviceImei);
		}
	}

	public static int getSocketSize() {
		return channelMaps.size();
	}

	/**
	 * 刷新设备的信号强度信息
	 * 
	 * @param channelId
	 */
	public static synchronized void updateDeviceSignal(String appId, String deviceIMEI, int signal,
			ChannelHandlerContext ctx) {
		try {
			ControlInfoEntity controlInfo = getDeviceControlInfoByIEMI(appId, deviceIMEI);
			if (controlInfo == null) {
				// 不太可能出现这个情况。redis里取不到数据，应该是过期了。而过期的原因，一定是设备没有发消息上来。而没消息上来的话，链路会断开。
				// 链路一旦断开，链路里就没有这个设备的对象信息。因此也没办法创建新的控制信息对象了。
				logger.error("严重错误，redis里的设备控制信息丢失！");
				if (ctx != null) {
					ctx.close();
				}
				return;
			} else {
				controlInfo.setSignal(signal);
			}
			// 把设备的信息放到redis里
			RedisUtil.addValue(DictEnum.RedisKey.DEVICE_CONTROL + appId + ":" + deviceIMEI,
					JSON.toJSONString(controlInfo), ServerConfig.READ_IDLE_TIME);
			logger.info("NettyChannelMapUtils.refreshDeviceControlInfoExpire()时间 ：" + ServerConfig.READ_IDLE_TIME);
		} catch (Exception e) {
			logger.error("NettyChannelMapUtils.refreshDeviceControlInfoExpire()出错，参数信息如下：");
			logger.error("channelMaps.size()=" + channelMaps.size());
			logger.error(e.getMessage(), e);
		}
	}
	
	/**
	 * 判断设备接收查询指令信息状态
	 * 
	 * @param channelId
	 */
	public static synchronized boolean deviceQueryStatus(String appId, String deviceIMEI,
			ChannelHandlerContext ctx) {
		try {
			ControlInfoEntity controlInfo = getDeviceControlInfoByIEMI(appId, deviceIMEI);
			if (controlInfo == null) {
				// 不太可能出现这个情况。redis里取不到数据，应该是过期了。而过期的原因，一定是设备没有发消息上来。而没消息上来的话，链路会断开。
				// 链路一旦断开，链路里就没有这个设备的对象信息。因此也没办法创建新的控制信息对象了。
				logger.error("严重错误，redis里的设备控制信息丢失！");
				if (ctx != null) {
					ctx.close();
				}
				return true;
			} else {
				if(controlInfo.getQuery() == 0) {
					return false;
				}else {
					return true;
				}
			}
		} catch (Exception e) {
			logger.error("NettyChannelMapUtils.deviceQueryStatus()出错，参数信息如下：");
			logger.error("channelMaps.size()=" + channelMaps.size());
			logger.error(e.getMessage(), e);
			return true;
		}
	}
	
	/**
	 * 刷新设备接收查询指令信息状态
	 * 
	 * @param channelId
	 */
	public static synchronized void updateDeviceQuery(String appId, String deviceIMEI,
			ChannelHandlerContext ctx) {
		try {
			ControlInfoEntity controlInfo = getDeviceControlInfoByIEMI(appId, deviceIMEI);
			if (controlInfo == null) {
				// 不太可能出现这个情况。redis里取不到数据，应该是过期了。而过期的原因，一定是设备没有发消息上来。而没消息上来的话，链路会断开。
				// 链路一旦断开，链路里就没有这个设备的对象信息。因此也没办法创建新的控制信息对象了。
				logger.error("严重错误，redis里的设备控制信息丢失！");
				if (ctx != null) {
					ctx.close();
				}
				return;
			} else {
				controlInfo.setQuery(1);
			}
			// 把设备的信息放到redis里
			RedisUtil.addValue(DictEnum.RedisKey.DEVICE_CONTROL + appId + ":" + deviceIMEI,
					JSON.toJSONString(controlInfo), ServerConfig.READ_IDLE_TIME);
			logger.info("NettyChannelMapUtils.updateDeviceQuery()设备已接收查询指令时间 ：" + ServerConfig.READ_IDLE_TIME);
		} catch (Exception e) {
			logger.error("NettyChannelMapUtils.refreshDeviceControlInfoExpire()出错，参数信息如下：");
			logger.error("channelMaps.size()=" + channelMaps.size());
			logger.error(e.getMessage(), e);
		}
	}

	/**
	 * 刷新设备在redis里的缓存时间
	 * 
	 * @param channelId
	 */
	public static void refreshDeviceControlInfoExpire(String appId, String deviceIMEI) {
		try {
			String imelKey = DEVICE_CONTROL + appId + ":" + deviceIMEI;
			Object redisObj = RedisUtil.getValue(imelKey);
			if (redisObj == null) {
				remove(appId, deviceIMEI);
				JSONObject sendJson = new JSONObject();
				sendJson.put("imei", deviceIMEI);
				CallBackInfo callBackInfo = new CallBackInfo(sendJson.toString(),
						ServerConfig.SUB_HTTPS_URL + "/deviceApi/updateRequestDate.do?imei=" + deviceIMEI);
				CallBackTaskQueue.getInstance().addCallBackMsg(callBackInfo);
			} else {
				RedisUtil.expire(imelKey, ServerConfig.READ_IDLE_TIME);
				logger.info("NettyChannelMapUtils.refreshDeviceControlInfoExpire()时间 ：" + ServerConfig.READ_IDLE_TIME);
				// String timeStampByMinute = Utils.timeStampByMinute();
				// if(timeStampByMinute.endsWith("3") || timeStampByMinute.endsWith("6") ||
				// timeStampByMinute.endsWith("9")) {
				// JSONObject sendJson = new JSONObject();
				// sendJson.put("imei", deviceIMEI);
				// CallBackInfo callBackInfo = new CallBackInfo(sendJson.toString(),
				// ServerConfig.SUB_HTTPS_URL + "?imei=" + deviceIMEI);
				// CallBackTaskQueue.getInstance().addCallBackMsg(callBackInfo);
				// }
			}
		} catch (Exception e) {
			logger.error("NettyChannelMapUtils.refreshDeviceControlInfoExpire()出错，参数信息如下：");
			logger.error(e.getMessage(), e);
		}
	}

	public static ControlInfoEntity getDeviceControlInfoByIEMI(String appId, String deviceIMEI) {
		try {
			Object redisObj = RedisUtil.getValue(DEVICE_CONTROL + appId + ":" + deviceIMEI);
			if (redisObj == null) {
				return null;
			} else {
				return (ControlInfoEntity) JSON.parseObject(redisObj.toString(), ControlInfoEntity.class);
			}
		} catch (Exception e) {
			logger.error("SocketChannelMap.getDeviceByIEMI()出错，参数信息如下：");
			logger.error("socketMap.size()=" + channelMaps.size());
			logger.error(e.getMessage(), e);
			return null;
		}
	}

	/**
	 * 非原子操作，需要同步 刷新设备在redis里的状态
	 * 
	 * @param deviceIMEI
	 * @param channelId
	 * @param channelStatus
	 */
	public static synchronized void refreshDeviceStatus(String appId, String deviceIMEI, Integer deviceChannelId,
			byte channelStatus, ChannelHandlerContext ctx) {
		ControlInfoEntity controlInfo = getDeviceControlInfoByIEMI(appId, deviceIMEI);
		if (controlInfo == null) {
			// 不太可能出现这个情况。redis里取不到数据，应该是过期了。而过期的原因，一定是设备没有发消息上来。而没消息上来的话，链路会断开。
			// 链路一旦断开，链路里就没有这个设备的对象信息。因此也没办法创建新的控制信息对象了。
			logger.error("严重错误，redis里的设备控制信息丢失！");
			if (ctx != null) {
				ctx.close();
			}
			return;
		} else {
			controlInfo.setChannelStatus(deviceChannelId, channelStatus);
		}
		logger.info("刷新redis refreshDeviceStatus=" + JSON.toJSONString(controlInfo));
		// 把设备的信息放到redis里
		RedisUtil.addValue(DictEnum.RedisKey.DEVICE_CONTROL + appId + ":" + deviceIMEI, JSON.toJSONString(controlInfo),
				ServerConfig.READ_IDLE_TIME);
	}
	
	/**
	 * 非原子操作，需要同步 检查设备在redis里锁定的预约状态
	 * 
	 * @param deviceIMEI
	 * @param channelId
	 * @param channelStatus
	 */
	public static synchronized void checkDeviceLockStatus(String appId, String deviceIMEI, Integer deviceChannelId,
			byte channelStatus, ChannelHandlerContext ctx) {
		ControlInfoEntity controlInfo = getDeviceControlInfoByIEMI(appId, deviceIMEI);
		logger.info("去redis里查找设备是否存在有预约锁定的状态=" + JSON.toJSONString(controlInfo));
		
		// 去redis里查找设备是否存在有预约锁定的状态
	      // key格式：[LOCK_ORDER_STATUS][DEVICE_IEMI]12345678[DEVICE_CHANNEL_INDEX]0  
	    String redisLockOrderKey = DictEnum.RedisKey.LOCK_ORDER_STATUS + appId + ":" + deviceIMEI + ":" + deviceChannelId;
	    Object redisObj = RedisUtil.getValue(redisLockOrderKey);
	    if(redisObj != null) {
//	    	JSONObject param = new JSONObject();
//			param.put("appId", APP_ID);
//			param.put("appSecret", APP_SECRET);
//			param.put("deviceIMEI", deviceIMEI);
//			param.put("deviceChannelIndex", deviceChannelIndex);
//			param.put("lockTocken", userOpenId);
//			param.put("lockTime", second);
//			String url = SysConfig.getInstance().getDeviceServerUrl() + DEVICE_LOCK;
//			return HttpUtil.dataServerRequest(url, param.toJSONString(), "utf-8", "application/json");
	    	
	    	//(LockDevicePara) JSON.parseObject(redisObj.toString(), LockDevicePara.class);
	        
	    	// 现在开始锁定设备
	    	JSONObject param = (JSONObject) redisObj;
			param.put("lockEndTime", new Date().getTime() + param.getIntValue("lockTime") * 1000);//锁定结束的时间
			param.put("lockTocken", param.getString("lockTocken"));
//	        DeviceLockInfoEntity deviceLockInfoEntity = new DeviceLockInfoEntity();
//	        deviceLockInfoEntity.setLockEndTime(new Date().getTime() + lockSecond * 1000);
//	        deviceLockInfoEntity.setLockTocken(lockDevicePara.getLockTocken());

			// key格式：[LOCK_STATUS][DEVICE_IEMI]12345678[DEVICE_CHANNEL_INDEX]0 
	        String redisLockKey = DictEnum.RedisKey.LOCK_STATUS + appId + ":" + deviceIMEI + ":" + deviceChannelId;
	        // 保存锁定信息到redis里
	        RedisUtil.addValue(redisLockKey, param.toString(), param.getIntValue("lockTime"));//锁定的时间，单位秒
	        
	        RedisUtil.remove(redisLockOrderKey);
	    }
	}

	/**
	 * 非原子操作，需要同步 刷新设备在redis里的状态
	 * 
	 * @param deviceIMEI
	 * @param channelId
	 * @param channelStatus
	 */
	public static synchronized void refreshDeviceErrorStatus(String appId, String deviceIMEI, Integer deviceChannelId,
			byte channelStatus, String errorCode, String errorText) {
		ControlInfoEntity controlInfo = getDeviceControlInfoByIEMI(appId, deviceIMEI);
		if (controlInfo == null) {
			// 不太可能出现这个情况。redis里取不到数据，应该是过期了。而过期的原因，一定是设备没有发消息上来。而没消息上来的话，链路会断开。
			// 链路一旦断开，链路里就没有这个设备的对象信息。因此也没办法创建新的控制信息对象了。
			logger.error("严重错误，redis里的设备控制信息丢失！");
			return;
		} else {
			controlInfo.setChannelErrorStatus(deviceChannelId, channelStatus, errorCode, errorText);
		}
		logger.info("刷新redis refreshDeviceErrorStatus=" + JSON.toJSONString(controlInfo));
		// 把设备的信息放到redis里
		RedisUtil.addValue(DictEnum.RedisKey.DEVICE_CONTROL + appId + ":" + deviceIMEI, JSON.toJSONString(controlInfo),
				ServerConfig.READ_IDLE_TIME);
	}

	/**
	 * 设置callback参数，数据返回时，需要取出来回调
	 * 
	 * @param deviceIMEI
	 * @param deviceChannelId
	 * @param callbackPara
	 */
	public static void setCallBackPara(String appId, String deviceIMEI, Integer deviceChannelId, String callbackPara,
			long timeout) {
		RedisUtil.addValue(CALLBACK_INFO + appId + ":" + deviceIMEI + ":" + deviceChannelId, callbackPara, timeout);
	}

	/**
	 * 获得保存的callback参数
	 * 
	 * @param deviceIMEI
	 * @param deviceChannelId
	 * @return
	 */
	public static String getCallBackPara(String appId, String deviceIMEI, Integer deviceChannelId, short workStatus,
			String callBackType) {
		JSONObject json = new JSONObject();
		switch (callBackType) {
		case DictEnum.CallBackType.START_RESULT:
			json.put("callBackType", callBackType);
			json.put("deviceIMEI", deviceIMEI);
			json.put("deviceChannelIndex", deviceChannelId);
			json.put("resultCode", getStartResultCode(workStatus));
			json.put("resultMsg", getStartResultMsg(workStatus));
			Object obj1 = RedisUtil.getValue(CALLBACK_INFO + appId + ":" + deviceIMEI + ":" + deviceChannelId);
			if (obj1 != null) {
				json.put("callbackPara", obj1.toString());
			}
			return json.toString();

		case DictEnum.CallBackType.STATUS_REPORT:
			json.put("callBackType", callBackType);
			json.put("deviceIMEI", deviceIMEI);
			json.put("deviceChannelIndex", deviceChannelId);
			json.put("resultCode", getDeviceStatusResultCode(workStatus));
			json.put("resultMsg", getDeviceStatusResultMsg(workStatus));
			Object obj2 = RedisUtil.getValue(CALLBACK_INFO + appId + ":" + deviceIMEI + ":" + deviceChannelId);
			if (obj2 != null) {
				json.put("callbackPara", obj2.toString());
			}
			return json.toString();

		default:
			return null;
		}
	}

	/**
	 * 获得保存的callback参数
	 * 
	 * @param deviceIMEI
	 * @param deviceChannelId
	 * @return
	 */
	public static String getCallBackPara(String appId, String deviceIMEI, Integer deviceChannelId, String resultCode,
			String resultMsg, String callBackType) {
		JSONObject json = new JSONObject();
		json.put("callBackType", callBackType);
		json.put("deviceIMEI", deviceIMEI);
		json.put("deviceChannelIndex", deviceChannelId);
		json.put("resultCode", resultCode);
		json.put("resultMsg", resultMsg);
		Object obj2 = RedisUtil.getValue(CALLBACK_INFO + appId + ":" + deviceIMEI + ":" + deviceChannelId);
		if (obj2 != null) {
			json.put("callbackPara", obj2.toString());
		}
		return json.toString();

	}
	
	/**
	 * 获得保存的callback参数
	 * 
	 * @param deviceIMEI
	 * @param deviceChannelId
	 * @return
	 * @throws Exception 
	 */
	public static Map<String, Object> queryBalance(String card) throws Exception {
		Map<String, Object> balanceMap = new HashMap<String, Object>();
		JSONObject cardJson = new JSONObject();
		cardJson.put("orgCard", card);
//		Map<String, String> map = new HashMap<String, String>();
//		map.put("orgCard", card);
		String respJson = HttpHelper.sendHttpPost(ServerConfig.SUB_HTTPS_URL + "/apiUser/getUserByCardId.do", cardJson.toString());
		JSONObject json = JSONObject.parseObject(respJson);
		int msgCode = json.getIntValue("msg_code");
		balanceMap.put("STATUS", 0);
		if(msgCode == 0) {
			balanceMap.put("STATUS", 1);
			JSONObject object = json.parseObject(json.getString("msg_data"));
			int score = object.getIntValue("score");
			int balance = object.getIntValue("balance");
			balanceMap.put("balance", balance);
			balanceMap.put("score", score);
		}
		return balanceMap;
	}
	
	public static Map<String, Object> queryBalance(String card, String deviceIMEI) throws Exception {
		JSONObject cardJson = new JSONObject();
		cardJson.put("orgCard", card);
		cardJson.put("deviceIMEI", deviceIMEI);
		
		Map<String, Object> balanceMap = new HashMap<String, Object>();
//		Map<String, String> map = new HashMap<String, String>();
//		map.put("orgCard", card);
		String respJson = HttpHelper.sendHttpPost(ServerConfig.SUB_HTTPS_URL + "/apiUser/getUserByOnlineCardId.do", cardJson.toString());
		JSONObject json = JSONObject.parseObject(respJson);
		int msgCode = json.getIntValue("msg_code");
		logger.info("queryBalance msgCode  》》》》》》》 " + msgCode);
		balanceMap.put("STATUS", 0);
		if(msgCode == 0) {
			balanceMap.put("STATUS", 1);
			JSONObject object = json.parseObject(json.getString("msg_data"));
			int score = object.getIntValue("score");
			int balance = object.getIntValue("balance");
			balanceMap.put("balance", balance);
			balanceMap.put("score", score);
		}else {
			balanceMap.put("balance", 0);
			balanceMap.put("score", 0);
		}
		return balanceMap;
	}
	
	/**
	 * 获得保存的callback参数
	 * 
	 * @param deviceIMEI
	 * @param deviceChannelId
	 * @return
	 * @throws Exception 
	 */
	public static void balanceSuccess(String card, int balance, int score) throws Exception {
		JSONObject cardJson = new JSONObject();
		cardJson.put("orgCard", card);
		cardJson.put("balance", balance);
		cardJson.put("score", score);	
		CallBackInfo callBackInfo = new CallBackInfo(cardJson.toString(),
				ServerConfig.SUB_HTTPS_URL + "/apiUser/subtractByCardId.do");
		CallBackTaskQueue.getInstance().addCallBackMsg(callBackInfo);
	}
	
	/**
	 * 充值成功，通知子平台创建消费订单，并扣除用户积分、余额
	 * 
	 * @param deviceIMEI
	 * @param deviceChannelId
	 * @return
	 * @throws Exception 
	 */
	public static String createCardRechargeOrder(String card, Long balance, String deviceMac) throws Exception {
		JSONObject cardJson = new JSONObject();
		cardJson.put("orgCard", card);
		cardJson.put("balance", balance);
		cardJson.put("device_mac", deviceMac);
		String respJson = HttpHelper.sendHttpPost(ServerConfig.SUB_HTTPS_URL + "/apiUser/createConsumeOrderByCardId.do", cardJson.toString());
		JSONObject json = JSONObject.parseObject(respJson);
		String msgCode = json.getString("msg_code");
		if(msgCode.equals("0")) {
			return "SUCCESS";
		}
		return "ERROR";
	}
	
	/**
	 * 在线卡消费订单，通知子平台创建消费订单，并扣除用户积分、余额
	 * 
	 * @param deviceIMEI
	 * @param deviceChannelId
	 * @return
	 * @throws Exception 
	 */
	public static void createOnlineCardRechargeConsumeOrder(String card, String transCsn, Long balance, String deviceMac,
			String appId, String deviceIMEI, ChannelHandlerContext ctx) throws Exception {
		JSONObject cardJson = new JSONObject();
		cardJson.put("orgCard", card);
		cardJson.put("transCsn", transCsn);
		cardJson.put("balance", balance);
		cardJson.put("device_mac", deviceMac);
//		String respJson = HttpHelper.sendHttpPost(ServerConfig.SUB_HTTPS_URL + "/apiUser/createOnlineConsumeOrderByCardId.do", cardJson.toString());
//		JSONObject json = JSONObject.parseObject(respJson);
//		String msgCode = json.getString("msg_code");
//		logger.info("createOnlineCardRechargeConsumeOrder msgCode 》》》》》》》 " + msgCode);
//		if(msgCode.equals("0")) {
//			return "SUCCESS";
//		}
//		return "ERROR";
		String sendUrl = ServerConfig.SUB_HTTPS_URL + "/apiUser/createOnlineConsumeOrderByCardId.do";
		CallBackInfo callBackInfo = new CallBackInfo(cardJson.toString(), sendUrl);
		CallBackTaskQueue.getInstance().addCallBackMsg(callBackInfo);
		//通道默认为0
		refreshDeviceStatus(appId, deviceIMEI, 0,
				DictEnum.DeviceChannelStatusCode.WORKING, ctx);
		removeOrder(appId, deviceIMEI, 0);
	}

	/**
	 * 发送回调消息给服务器
	 * 
	 * @param appId
	 * @param deviceIMEI
	 * @param deviceChannelId
	 * @param callBackType
	 * @param cmd
	 * @param sendUrl
	 * @param statusDictMap
	 *            错误状态集合
	 */
	public static void callBackSendStatus(String appId, String deviceIMEI, Integer deviceChannelId, String callBackType,
			String cmd, String sendUrl, Map<String, StatusDictEntity> statusDictMap, ChannelHandlerContext ctx) {
		// 这属于投币状态，不更新redis
		if (DictEnum.DeviceStatusCode.STARTING.equals(cmd)) {
			String sendMessage = getCallBackPara(appId, deviceIMEI, deviceChannelId, DictEnum.StartResultCode.SUCCESS,
					callBackType);
			CallBackInfo callBackInfo = new CallBackInfo(sendMessage, sendUrl);
			CallBackTaskQueue.getInstance().addCallBackMsg(callBackInfo);
		} else {
			ControlInfoEntity controlInfoEntity = getDeviceControlInfoByIEMI(appId, deviceIMEI);
			if (controlInfoEntity != null) {
				short status = controlInfoEntity.getChannelStatus(deviceChannelId);
				logger.info("通道=" + deviceChannelId + ", 状态=" + status);
				// 工作中
				if (DictEnum.DeviceStatusCode.WORKING.equals(cmd)) {
					// 如果redis里面是空闲中，要改变状态并且回调
					if (DictEnum.DeviceChannelStatusCode.FREE == status) {
						String sendMessage = getCallBackPara(appId, deviceIMEI, deviceChannelId,
								DictEnum.DeviceChannelStatusCode.START_SUCCESS, callBackType);
						CallBackInfo callBackInfo = new CallBackInfo(sendMessage, sendUrl);
						CallBackTaskQueue.getInstance().addCallBackMsg(callBackInfo);
						refreshDeviceStatus(appId, deviceIMEI, deviceChannelId,
								DictEnum.DeviceChannelStatusCode.WORKING, ctx);
						removeOrder(appId, deviceIMEI, deviceChannelId);
					} else if (DictEnum.DeviceChannelStatusCode.DEVICE_ERROR == status) {
						String sendMessage = getCallBackPara(appId, deviceIMEI, deviceChannelId,
								DictEnum.DeviceChannelStatusCode.WORKING, callBackType);
						CallBackInfo callBackInfo = new CallBackInfo(sendMessage, sendUrl);
						CallBackTaskQueue.getInstance().addCallBackMsg(callBackInfo);
						refreshDeviceStatus(appId, deviceIMEI, deviceChannelId,
								DictEnum.DeviceChannelStatusCode.WORKING, ctx);
						removeOrder(appId, deviceIMEI, deviceChannelId);
					} else {
						NettyChannelMapUtils.refreshDeviceControlInfoExpire(appId, deviceIMEI);// 设备有消息上来，在redis里做延期
					}
				} else if (DictEnum.DeviceStatusCode.FREE.equals(cmd)) {
					// 如果redis里面是空闲工作中，要改变状态并且回调
					if (DictEnum.DeviceChannelStatusCode.WORKING == status
							|| DictEnum.DeviceChannelStatusCode.DEVICE_ERROR == status) {
						String sendMessage = getCallBackPara(appId, deviceIMEI, deviceChannelId,
								DictEnum.DeviceChannelStatusCode.WORK_DONE, callBackType);
						CallBackInfo callBackInfo = new CallBackInfo(sendMessage, sendUrl);
						CallBackTaskQueue.getInstance().addCallBackMsg(callBackInfo);
						refreshDeviceStatus(appId, deviceIMEI, deviceChannelId, DictEnum.DeviceChannelStatusCode.FREE,
								ctx);
						
					} else {
						NettyChannelMapUtils.refreshDeviceControlInfoExpire(appId, deviceIMEI);// 设备有消息上来，在redis里做延期
					}
				} else {
					StatusDictEntity statusDictEntity = statusDictMap.get(cmd);
					if (statusDictEntity != null) {
						if (DictEnum.DeviceChannelStatusCode.FREE == status) {
							String sendMessageWork = getCallBackPara(appId, deviceIMEI, deviceChannelId,
									DictEnum.DeviceChannelStatusCode.START_SUCCESS, callBackType);
							CallBackInfo callBackInfoWork = new CallBackInfo(sendMessageWork, sendUrl);
							CallBackTaskQueue.getInstance().addCallBackMsg(callBackInfoWork);

							String sendMessage = getCallBackPara(appId, deviceIMEI, deviceChannelId,
									statusDictEntity.getMsgCode(), statusDictEntity.getMsgText(), callBackType);
							CallBackInfo callBackInfo = new CallBackInfo(sendMessage, sendUrl);
							callBackInfo.setSleepTime(3000);
							CallBackTaskQueue.getInstance().addCallBackMsg(callBackInfo);
						} else {
							removeOrder(appId, deviceIMEI, deviceChannelId);
							String sendMessage = getCallBackPara(appId, deviceIMEI, deviceChannelId,
									statusDictEntity.getMsgCode(), statusDictEntity.getMsgText(), callBackType);
							CallBackInfo callBackInfo = new CallBackInfo(sendMessage, sendUrl);
							CallBackTaskQueue.getInstance().addCallBackMsg(callBackInfo);
						}
						refreshDeviceErrorStatus(appId, deviceIMEI, deviceChannelId,
								DictEnum.DeviceChannelStatusCode.DEVICE_ERROR, statusDictEntity.getMsgCode(),
								statusDictEntity.getMsgText());
					} else {
						NettyChannelMapUtils.refreshDeviceControlInfoExpire(appId, deviceIMEI);// 设备有消息上来，在redis里做延期
					}
				}
			}
		}
	}

	public static void callBackSendStatusError(String appId, String deviceIMEI, Integer deviceChannelId,
			String callBackType, String cmd, String sendUrl, Map<String, StatusDictEntity> statusDictMap,
			ChannelHandlerContext ctx) {
		StatusDictEntity statusDictEntity = statusDictMap.get(cmd);
		if (statusDictEntity != null) {
			refreshDeviceErrorStatus(appId, deviceIMEI, deviceChannelId, DictEnum.DeviceChannelStatusCode.DEVICE_ERROR,
					statusDictEntity.getMsgCode(), statusDictEntity.getMsgText());
		}
	}

	private static String getStartResultCode(short workStatus) {
		switch (workStatus) {
		case DictEnum.StartResultCode.SUCCESS:
			return "SUCCESS";
		case DictEnum.StartResultCode.FAIL:
			return "FAIL";
		default:
			return "FAIL";
		}
	}

	private static String getStartResultMsg(short workStatus) {
		switch (workStatus) {
		case DictEnum.StartResultCode.SUCCESS:
			return DictEnum.StartResultMsg.SUCCESS;
		case DictEnum.StartResultCode.FAIL:
			return DictEnum.StartResultMsg.FAIL;
		default:
			return DictEnum.StartResultMsg.FAIL;
		}
	}

	private static String getDeviceStatusResultCode(short workStatus) {
		switch (workStatus) {
		case DictEnum.DeviceChannelStatusCode.DEVICE_CHANNEL_ERROR:
			return "DEVICE_CHANNEL_ERROR";
		case DictEnum.DeviceChannelStatusCode.DEVICE_ERROR:
			return "DEVICE_ERROR";
		case DictEnum.DeviceChannelStatusCode.FREE:
			return "FREE";
		case DictEnum.DeviceChannelStatusCode.START_SUCCESS:
			return "START_SUCCESS";
		case DictEnum.DeviceChannelStatusCode.WORK_DONE:
			return "WORK_DONE";
		case DictEnum.DeviceChannelStatusCode.WORKING:
			return "WORKING";
		default:
			return "UNKNOW_ERROR";
		}
	}

	private static String getDeviceStatusResultMsg(short workStatus) {
		switch (workStatus) {
		case DictEnum.DeviceChannelStatusCode.DEVICE_CHANNEL_ERROR:
			return DictEnum.DeviceChannelStatusMsg.DEVICE_CHANNEL_ERROR;
		case DictEnum.DeviceChannelStatusCode.DEVICE_ERROR:
			return DictEnum.DeviceChannelStatusMsg.DEVICE_ERROR;
		case DictEnum.DeviceChannelStatusCode.FREE:
			return DictEnum.DeviceChannelStatusMsg.FREE;
		case DictEnum.DeviceChannelStatusCode.START_SUCCESS:
			return DictEnum.DeviceChannelStatusMsg.START_SUCCESS;
		case DictEnum.DeviceChannelStatusCode.WORK_DONE:
			return DictEnum.DeviceChannelStatusMsg.WORK_DONE;
		case DictEnum.DeviceChannelStatusCode.WORKING:
			return DictEnum.DeviceChannelStatusMsg.WORKING;
		default:
			return DictEnum.DeviceChannelStatusMsg.UNKNOW_ERROR;
		}
	}

	private static void removeOrder(String appId, String deviceIMEI, Integer deviceChannelId) {
		Object obj1 = RedisUtil.getValue(CALLBACK_INFO + appId + ":" + deviceIMEI + ":" + deviceChannelId);
		if (obj1 != null) {
			JSONObject json = JSONObject.parseObject(obj1.toString());
			String orderId = json.getString("orderId");
			String meskey = DictEnum.RedisKey.ORDER + ":" + orderId;
			if (RedisUtil.getValue(meskey) != null) {
				RedisUtil.remove(meskey);
				meskey = meskey.replace(DictEnum.RedisKey.ORDER, DictEnum.RedisKey.ORDER_RESEND);
				RedisUtil.remove(meskey);
			}
		}
	}

	public static void main(String[] args) throws Exception {
		JSONObject cardJson = new JSONObject();
		cardJson.put("imei", "1111111111111111");
		cardJson.put("num", 3);
		String respJson = HttpHelper.sendHttpPost("http://192.168.20.153:8091/SubManager-web/apiOrgOutSoltOrder/insert.do", cardJson.toString());
		JSONObject json = JSONObject.parseObject(respJson);
		System.out.println(json);
	}
}
